package io.gitee.macxiang.utils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.function.Function;

import org.apache.commons.lang3.ObjectUtils.Null;
import org.springframework.context.ApplicationContext;

import com.alibaba.fastjson2.JSONObject;

import io.gitee.macxiang.config.MacConfig;
import io.gitee.macxiang.entity.ApiResponse;

class Methods {
  final public Object bean;
  final public HashMap<String, Method> methods = new HashMap<>();

  public Methods(Object bean) {
    this.bean = bean;
  }
}

public class utils extends Datas {
  /** 加密相关 */
  public static class crypt extends Crypt {
  };

  /** 微信相关,使用的话,最好采用继承的方式,然后使用init方法进行初始化配置 */
  public static class WeChat extends io.gitee.macxiang.utils.WeChat {
  }

  /** 自动接口,只将post请求自动化了,get可以考虑自行实现 */
  public static class controller {
    private static final HashMap<String, Methods> postMap = new HashMap<>();
    private static final List<String> serviceNames = new ArrayList<>();
    private static Function<String, ?> converter = null;

    /** 清理全部自动的方法 */
    public static void clearMethod() {
      postMap.clear();
    }

    /**向自动接口post中添加方法;
     * @param tableName   接口名称
     * @param bean        接口对应的bean
     * @param method      对应方法,参数必须是 JSONObject request, String token
     */
    private static void addPost(String tableName, Object bean, Method method) {
      Methods table = postMap.get(tableName);
      if (table == null) {
        table = new Methods(bean);
        postMap.put(tableName, table);
      }
      table.methods.put(method.getName(), method);
    }

    /**自动执行 接口方法
     * @param tableName   接口名称
     * @param methodName  接口里的方法名称
     * @param request     请求内容
     * @param token       请求时携带的请求头token,用以标识用户
     * @return            方法执行完后的返回值
     */
    public static Object post(String tableName, String methodName, JSONObject request, String token) {
      if (converter == null) {
        System.err.println("初始化未完成?转换函数失效");
        return null;
      }
      Methods table = postMap.get(tableName);
      if (table == null) {
        throw new RuntimeException("接口不存在:" + tableName);
      }
      Method method = table.methods.get(methodName);
      if (method == null) {
        throw new RuntimeException(String.format("接口[%s]方法不存在:%s", tableName, methodName));
      }
      try {
        return method.invoke(table.bean, request, converter.apply(token));
      } catch (Exception e) {
        String s = errString(e);
        if (parseJavaObject(s, ApiResponse.class) == null && !containsNonAscii(s)) {// 只有非Ascii字符,需要打印出来
          System.err.println(String.format("接口[%s]方法(%s)抛出异常:%s", tableName, methodName, s));
        }
        try {
          throw e;
        } catch (Exception ee) {
          throw new RuntimeException(s);
        }
      }
    }

    /**初始化自动控制器映射到service对应方法中
     * @param <T>                 用于限制转换器与转换器返回值对应
     * @param converter           转换器函数,接受一个String参数(token); 返回 Type所设定的类型
     * @param converterReturnType 转换器返值回类型
     * @param applicationContext  Spring的ctx;用于获取配置文件信息;
     */
    public static <T> void init(Function<String, T> converter, Class<T> converterReturnType,
        ApplicationContext applicationContext) {
      if (converter == null || converterReturnType == null || applicationContext == null) {
        System.err.println("添加自动处理控制器时,参数不能为null");
        return;
      }
      controller.converter = converter;
      final MacConfig config = applicationContext.getBean(MacConfig.class);
      if (config == null) {
        System.err.println("没有获取到配置信息");
        return;
      }
      for (String cn : serviceNames) {
        try {
          Class<?> service = Class.forName(cn);
          Object bean = applicationContext.getBean(service);
          Method[] methods = service.getMethods();
          for (Method method : methods) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 2 &&
                parameterTypes[0].equals(JSONObject.class) &&
                parameterTypes[1].equals(converterReturnType)) {
              String name = cn.replaceAll(config.controller.regex, config.controller.replacement);
              if (config.controller.fristLower) {
                name = (name.charAt(0) == '_' ? "" : Character.toLowerCase(name.charAt(0))) + name.substring(1);
              }
              addPost(name, bean, method);
            }
          }
        } catch (Exception e) {
          System.out.println(cn);
          e.printStackTrace();
        }
      }
    }

    /**向自动控制器添加需要映射的服务类名(完整类名);一般情况下,所有自动生成的服务类都会被自动添加进去;
     * 注意:需要在init方法前调用;
     * @param serviceName 完整服务类名
     */
    public static void addServiceName(String serviceName) {
      serviceNames.add(serviceName);
    }

    /**清理所有服务类名 */
    public static void clearServiceNames() {
      serviceNames.clear();
    }
  }

  // #region out方法 开始
  public static Null out(Exception e) {
    // throw new RuntimeException();
    String s = errString(e);
    if (parseJavaObject(s, ApiResponse.class) == null && !containsNonAscii(s)) {// 只有非Ascii字符,需要打印出来
      e.printStackTrace();
    }
    try {
      throw e;
    } catch (Exception ee) {
      throw new RuntimeException(s);
    }
  }

  public static Null out(ApiResponse msg) {
    throw new RuntimeException(msg.toString());
  }

  public static Null out(String message) {
    throw new RuntimeException(new ApiResponse(message).toString());
  }

  public static Null out(String message, String type) {
    throw new RuntimeException(new ApiResponse(message, type).toString());
  }

  public static Null out(String message, int code) {
    throw new RuntimeException(new ApiResponse(message, code).toString());
  }

  public static Null out(String message, String type, int code) {
    throw new RuntimeException(new ApiResponse(message, type, code).toString());
  }

  public static Null out(Object data) {
    throw new RuntimeException(new ApiResponse(data).toString());
  }

  public static Null out(Object data, String message) {
    throw new RuntimeException(new ApiResponse(data, message).toString());
  }

  public static Null out(Object data, String message, String type) {
    throw new RuntimeException(new ApiResponse(data, message, type).toString());
  }

  public static Null out(Object data, String message, int code) {
    throw new RuntimeException(new ApiResponse(data, message, code).toString());
  }

  public static Null out(Object data, String message, String type, int code) {
    throw new RuntimeException(new ApiResponse(data, message, type, code).toString());
  }

  // #endregion out方法 结束
}
